<?php
/**
 * Here the class '\UF\Config\Reader\Xml' is defined.
 *
 * @category   UniKap-Framework
 * @package    Config
 * @subpackage Reader
 * @author     Ulf -UniKado- Kadner <ulfikado@gmail.com>
 * @since      2014-03-29 16:17
 * @version    0.1
 */

namespace UF\Config\Reader
{

    /**
     * A configuration reader, working with XML config files.
     *
     * Each config XML file must contain config elements mit the attributes 'name', 'type' and 'strict'. The values
     * must be strored as a element string
     * (<code>&lt;Config name="foo" type="string" strict="0"&gt;The value here&lt;/Config&gt;</code>)
     *
     * @since      v0.1
     */
    class Xml implements \UF\Config\IReader
    {

        # <editor-fold defaultstate="collapsed" desc="- - - -   P R I V A T E   F I E L D S   - - - - - - - - - - - - - - - - - - - - -">

        private $file;
        private $rootElementName;
        private $configElementName;
        /**
         * @var SimpleXmlElement[] Array
         */
        private $xml;
        private $cpos;

        # </editor-fold>

        # <editor-fold defaultstate="collapsed" desc="- - - >   C O N S T R U C T O R   - - - - - - - - - - - - - - - - - - - - - - - -">

        /**
         * Inits a new instance.
         *
         * @param  string $xmlFile The XML file, containing the configuration data
         * @param  string $rootElementName The name of the root XML element.
         * @param  string $configElementName The name of the config XML elements.
         * @since  v0.1
         */
        public function __construct( $xmlFile, $rootElementName='Configs', $configElementName='Config' )
        {
            $this->file = $xmlFile;
            $this->rootElementName = empty($rootElementName)     ? 'Configs' : $rootElementName;
            $this->configElementName = empty($configElementName) ? 'Config'  : $configElementName;
            $this->reload();
        }

        # </editor-fold>

        # <editor-fold defaultstate="collapsed" desc="- - - -   I M P L E M E N T   ' \ U F \ C o n f i g \ I R e a d e r '   - - - - -">

        /**
         * Reads the complete next config value from defined source and returns it as a {@see \UF\Config\Value}.
         *
         * @return \UF\Config\Value or bool FALSE if no readable value exists
         * @since  v0.1
         */
        public function read()
        {
            $result = false;
            while ( true )
            {
                if ( !$this->canRead() ) { break; }
                $xml = $this->xml[$this->cpos];
                ++$this->cpos;
                if ( !isset($xml['name']) || !isset($xml['type']) || !isset($xml['strict']) ) { continue; }
                $valueStr = (string) $xml;
                $value = null;
                if ( $valueStr != '<NULL>' ) {
                    $value = \UF\TypeTool::StrToType( $valueStr, (string)$xml['type'] ); }
                $result = new \UF\Config\Value(
                    (string) $xml['name'], $value, ( (string) $xml['strict'] ) == '1', (string) $xml['type'] );
                break;
            }
            return $result;
        }

        /**
         * Reads all currently unread configuration values and returns it as a config value collection.
         *
         * @return \UF\Config\ValueCollection
         * @since  v0.1
         */
        public function readAll()
        {
            $result = new \UF\Config\ValueCollection();
            for ( ; $this->cpos < \count($this->xml); ++$this->cpos )
            {
                $xml = $this->xml[$this->cpos];
                if ( !isset($xml['name']) || !isset($xml['type']) || !isset($xml['strict']) ) { continue; }
                $name = (string)$xml['name'];
                $valueStr = (string)$xml;
                $value = null;
                if ( $valueStr != '<NULL>' ) {
                    $value = \UF\TypeTool::StrToType( $valueStr, (string)$xml['type'] ); }
                $result[$name] = new \UF\Config\Value(
                    $name, $value, ( (string) $xml['strict']) == '1', (string) $xml['type'] );
            }
            return $result;
        }

        /**
         * Returns all 3 options of th e reader as a associative array. Options are 'file', 'rootElementName'
         * and 'configElementName'.
         *
         * @return array
         * @since  v0.1
         */
        public function getOptions()
        {
            return array(
                'file' => $this->file,
                'rootElementName' => $this->rootElementName,
                'configElementName' => $this->configElementName
            );
        }

        /**
         * Returns if the current read is open (in read mode)
         *
         * @return bool
         * @since  v0.1
         */
        public function isOpen()
        {
            return ( \count( $this->xml ) > 0 ) && $this->canRead();
        }

        /**
         * Returns if the reader has something to read.
         *
         * @return bool
         * @since  v0.1
         */
        public function canRead()
        {
            return $this->cpos < \count( $this->xml );
        }

        /**
         * Resets the reading pointer to the source start.
         *
         * @since  v0.1
         */
        public function reset()
        {
            $this->cpos = 0;
        }

        /**
         * Reloads all data from the current used source.
         *
         * @since  v0.1
         */
        public function reload()
        {
            $this->cpos = 0;
            if ( !\file_exists( $this->file ) ) { $this->xml = array(); return; }
            try
            {
                $xml = \simplexml_load_file( $this->file );
                $this->xml = array();
                if ( !isset( $xml->Config ) ) { return; }
                foreach ($xml as $item) { $this->xml[] = $item; }
            }
            catch ( \Exception $ex ) { $ex = null; $this->xml = array(); }
        }

        # </editor-fold>

    }

}

